/*******************************************
 * Copyright 2007 Aphrodite Framework for javaME .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * Email : owner@aphroditeme.dev.java.net
 * Contributors:
 *     Kent Elton - initial API and implementation
 *******************************************/

package com.aphrodite.image;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

/**
 * Image manipulation routines
 * <p>
 * The efficiency of alpha processing and rgb data extraction is platform
 * dependent
 * </p>
 * 
 * @author kentelt
 * 
 */
public final class ImageManipulation {
	/**
	 * @see #shiftImageColor(Image, int)
	 */
	public static final int SHIFT_BLUE_TO_GREEN = 5;

	/**
	 * @see #shiftImageColor(Image, int)
	 */
	public static final int SHIFT_BLUE_TO_RED = 4;
	/**
	 * @see #shiftImageColor(Image, int)
	 */
	public static final int SHIFT_GREEN_TO_BLUE = 2;
	/**
	 * @see #shiftImageColor(Image, int)
	 */
	public static final int SHIFT_GREEN_TO_RED = 3;
	/**
	 * @see #shiftImageColor(Image, int)
	 */
	public static final int SHIFT_RED_TO_BLUE = 1;
	/**
	 * @see #shiftImageColor(Image, int)
	 */
	public static final int SHIFT_RED_TO_GREEN = 0;
	/**
	 * Apply sepia to an image
	 * <p>
	 * Sepia is a dark brown-grey color, named after the rich brown pigment
	 * derived from the ink sac of the common cuttlefish Sepia.<br/> Sepia with
	 * a <code>depth</code> of 10
	 * <table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/before.png"></td>
	 * <td><img src="../../../resources/sepia.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @see com.aphrodite.image.ColorUtil#applySepia(int, int)
	 * @param image
	 * @param depth
	 * @return
	 * @throws NullPointerException
	 *             if image is null
	 */
	public static Image applySepia(Image image, int depth) {
		if (image == null) {
			throw new NullPointerException();
		}
		int w = image.getWidth();
		int h = image.getHeight();
		int[] data = ImageUtil.getImageRGB(image, w, h);
		applySepia(data, depth);
		return Image.createRGBImage(data, w, h, true);
	}

	/**
	 * 
	 * @param argb
	 * @param depth
	 */
	public static void applySepia(int[] argb, int depth) {
		for (int i = 0; i < argb.length; i++) {
			argb[i] = ColorUtil.applySepia(argb[i], depth);
		}
	}

	/**
	 * Removes the transparency of an image
	 * 
	 * @param color
	 *            image rgb array
	 */
	public static final void clearTransparency(int[] color) {
		for (int i = 0; i < color.length; i++) {
			color[i] = ColorUtil.clearTransparency(color[i]);
		}

	}

	/**
	 * Uses color averaging method to convert to grayscale
	 * <p>
	 * 
	 * <table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/gray.png"></td>
	 * <td><img src="../../../resources/grayconv.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * Its worth mentioning that grayscale images are lighter since their color
	 * depth is just 8-bit
	 * 
	 * @see com.aphrodite.image.ColorUtil#covertToGrayscale(int)
	 * @param image
	 * @return
	 */
	public static final Image convertToGrayScale(Image image) {
		int w = image.getWidth();
		int h = image.getHeight();
		int[] data = ImageUtil.getImageRGB(image, w, h);
		convertToGrayScale(data);
		return Image.createRGBImage(data, w, h, true);
	}

	/**
	 * @see com.aphrodite.image.ColorUtil#covertToGrayscale(int)
	 * @param argb
	 */
	public static final void convertToGrayScale(int[] argb) {
		for (int i = 0; i < argb.length; i++) {
			argb[i] = ColorUtil.covertToGrayscale(argb[i]);
		}
	}

	public final static Image convertToHSV(Image image) {
		int[] rgbData = ImageUtil.getImageRGB(image, image.getWidth(), image.getHeight());
		convertToHSV(rgbData);
		return Image.createRGBImage(rgbData, image.getWidth(), image.getHeight(), true);
	}

	/**
	 * Convert image to HSV color model
	 * 
	 * @param argb
	 */
	public static void convertToHSV(int[] argb) {
		for (int i = 0; i < argb.length; i++) {
			int nR = ImageUtil.getRedComponent(argb[i]);
			int nG = ImageUtil.getGreenComponent(argb[i]);
			int nB = ImageUtil.getBlueComponent(argb[i]);
			int nMax, nMid, nMin;
			int nHueOffset;
			// determine color order
			if (nR > nG && nR > nB) {
				// red is max
				nMax = nR;
				nHueOffset = 0;
				if (nG > nB) {
					nMid = nG;
					nMin = nB;
				} else {
					nMid = nB;
					nMin = nG;
				}
			} else if (nG > nR && nG > nB) {
				// green is max
				nMax = nG;
				nHueOffset = 80;
				if (nR > nB) {
					nMid = nR;
					nMin = nB;
				} else {
					nMid = nB;
					nMin = nR;
				}
			} else {
				// blue is max
				nMax = nB;
				nHueOffset = 160;
				if (nR > nG) {
					nMid = nR;
					nMin = nG;
				} else {
					nMid = nG;
					nMin = nR;
				}
			}
			// if the max value is Byte.MIN_VALUE the RGB value
			// = 0 so the HSV value = 0 and needs no change.
			if (nMax > Byte.MIN_VALUE) {
				if (nMax == nMin) {
					// color is gray. Hue, saturation are 0.
					argb[i] = ColorUtil.toRGB(Byte.MIN_VALUE, Byte.MIN_VALUE, (byte) nMax);
				} else {
					// compute hue scaled from 0-240.
					int nHue = Math.min(239, nHueOffset + (40 * (nMid - nMin)) / (nMax - nMin));
					// compute saturation scaled from 0-255.
					int nSat = Math.min(255, (256 * (nMax - nMin)) / (nMax - Byte.MIN_VALUE));
					argb[i] = ColorUtil.toRGB((byte) (nHue + Byte.MIN_VALUE), (byte) (nSat + Byte.MIN_VALUE),
							(byte) nMax);
				}
			}
		}
	}

	/**
	 * Create a reflection of an image
	 * <p>
	 * Reflection with <code>bgColor=0xffffff</code> and
	 * <code> reflectionHeight=20</code>
	 * <table border="0">
	 * <tr>
	 * <td>
	 * <img src="../../../resources/gray.png"></td>
	 * <td><img src="../../../resources/reflection.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @param image
	 *            source image
	 * @param bgColor
	 *            background color. 0x00FFFFFF if its transparent
	 * @param reflectionHeight
	 *            height of the reflection
	 * @return Image
	 */
	public static final Image createReflection(Image image, int bgColor, int reflectionHeight) {
		// ############ tested by $t3p#3n on 29-july #################/
		int w = image.getWidth();
		int h = image.getHeight();

		Image reflectedImage = Image.createImage(w, h + reflectionHeight);

		Graphics g = reflectedImage.getGraphics();

		g.setColor(bgColor);
		g.fillRect(0, 0, w, h + reflectionHeight);

		g.drawImage(image, 0, 0, Graphics.TOP | Graphics.LEFT);

		int[] rgba = new int[w];
		int currentY = -1;

		for (int i = 0; i < reflectionHeight; i++) {
			int y = (h - 1) - (i * h / reflectionHeight);

			if (y != currentY)
				image.getRGB(rgba, 0, w, 0, y, w, 1);

			int alpha = 0xff - (i * 0xff / reflectionHeight);

			for (int j = 0; j < w; j++) {
				int origAlpha = (rgba[j] >> 24);
				int newAlpha = (alpha & origAlpha) * alpha / 0xff;

				rgba[j] = (rgba[j] & 0x00ffffff);
				rgba[j] = (rgba[j] | (newAlpha << 24));
			}

			g.drawRGB(rgba, 0, w, 0, h + i, w, 1, true);
		}
		return reflectedImage;
	}

	/**
	 * 
	 * @param image
	 * @param nDim
	 *            >256 brightens the mask area
	 * @param x
	 *            point of the mask rectangle(always within the image)
	 * @param y
	 *            point of the mask rectangle(always within the image)
	 * @param width
	 *            of the mask rectangle(always within the image)
	 * @param height
	 *            of the mask rectangle(always within the image)
	 * @return
	 */
	// public static Image dimImage(Image image, int nDim, int x, int y, int
	// width, int height) {
	// int[] src = ImageUtil.getImageRGB(image, image.getWidth(),
	// image.getHeight());
	// for (int i = 0; i < height; i++) {
	// for (int j = 0; j < width; j++) {
	//
	// }
	// }
	// int[] dst = new int[image.getWidth() * image.getHeight()];
	// for (int i = 0; i < image.getHeight(); i++) {
	// for (int j = 0; j < image.getWidth(); j++) {
	// int nColorPixel = src[i * image.getWidth() + j];
	// // if (isMasked(src, image.getWidth(), i, j)) {
	// int nRed = ImageUtil.getRedComponent(nColorPixel);
	// int nGreen = ImageUtil.getGreenComponent(nColorPixel);
	// int nBlue = ImageUtil.getBlueComponent(nColorPixel);
	// nRed = Math.max(Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, (nRed * nDim) >>
	// 8));
	// nGreen = Math.max(Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, (nGreen *
	// nDim) >> 8));
	// nBlue = Math.max(Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, (nBlue * nDim)
	// >> 8));
	// dst[i * image.getWidth() + j] = ColorUtil.toRGB((byte) nRed, (byte)
	// nGreen, (byte) nBlue);
	// // } else {
	// // dst[i * image.getWidth() + j] = nColorPixel;
	// // }
	// }
	// }
	// return Image.createRGBImage(dst, image.getWidth(), image.getHeight(),
	// false);
	// // super.setOutput(rgbOutput);
	//
	// }
	private static void doChannelMask(int[] argb, int mask) {
		for (int i = 0; i < argb.length; i++) {
			argb[i] = (argb[i] & mask);
			// restore aplha that is lost
			argb[i] = (argb[i] | 0xff000000);
		}
	}

	/**
	 * 
	 * @param image
	 * @param xOffset
	 *            of the shadow
	 * @param yOffset
	 *            of the shadow
	 * @param size
	 * @param innerColor
	 *            of the shadow
	 * @param outerColor
	 *            of the shadow
	 * @return
	 */
	public static final Image dropShadow(Image image, int xOffset, int yOffset, int size, int innerColor, int outerColor) {
		int w = image.getWidth();
		int h = image.getHeight();
		int[] data = ImageUtil.getImageRGB(image, w, h);
		dropShadow(data, w, h, xOffset, yOffset, size, innerColor, outerColor);
		return Image.createRGBImage(data, w, h, true);
	}

	/**
	 * <p>
	 * Paints a dropshadow behind a given ARGB-Array, whereas you are able to
	 * specify the shadows inner and outer color.
	 * </p>
	 * <p>
	 * Note that the dropshadow just works for fully opaque pixels and that it
	 * needs a transparent margin to draw the shadow.
	 * </p>
	 * <p>
	 * Choosing the same inner and outer color and varying the transparency is
	 * recommended. Dropshadow just works for fully opaque pixels.
	 * </p>
	 * 
	 * @param argbData
	 *            of the image
	 * @param width
	 *            of the image
	 * @param height
	 *            of the image
	 * @param xOffset
	 *            use this for finetuning the shadow's horizontal position.
	 *            Negative values move the shadow to the left.
	 * @param yOffset
	 *            use this for finetuning the shadow's vertical position.
	 *            Negative values move the shadow to the top.
	 * @param size
	 *            use this for finetuning the shadows radius.
	 * @param innerColor
	 *            the inner color of the shadow, which should be less opaque
	 *            than the text.
	 * @param outerColor
	 *            the outer color of the shadow, which should be less than
	 *            opaque the inner color.
	 */
	public final static void dropShadow(int[] argbData, int width, int height, int xOffset, int yOffset, int size,
			int innerColor, int outerColor) {
		// additional Margin for the image because of the shadow
		int iLeft = size - xOffset < 0 ? 0 : size - xOffset;
		int iRight = size + xOffset < 0 ? 0 : size + xOffset;
		int iTop = size - yOffset < 0 ? 0 : size - yOffset;
		int iBottom = size + yOffset < 0 ? 0 : size + yOffset;

		// set colors
		int[] gradient = ColorUtil.getGradient(innerColor, outerColor, size);

		// walk over the text and look for non-transparent Pixels
		for (int ix = -size + 1; ix < size; ix++) {
			for (int iy = -size + 1; iy < size; iy++) {
				// int gColor=gradient[ Math.max(Math.abs(ix),Math.abs(iy))];
				// int gColor=gradient[(Math.abs(ix)+Math.abs(iy))/2];

				// compute the color and draw all shadowPixels with offset (ix,
				// iy)
				// int r = 0;
				// if
				// (com.aphrodite.util.StringUtil.equalsIgnoreCase(DeviceInfo.
				// getMEConfiguration(),"CLDC-1.1")){
				// r = (int) Math.sqrt(ix * ix + iy * iy); // TODO: this
				// might
				// } else if (DeviceInfo.getMEConfiguration(),"CLDC-1.0") {
				int r = (Math.abs(ix) + Math.abs(iy)) / 2;
				// }
				if (r < size) {
					int gColor = gradient[r];

					for (int col = iLeft, row; col < width/* +iLeft */- iRight; col++) {
						for (row = iTop; row < height - iBottom/* +iTop */- 1; row++) {

							// draw if an opaque pixel is found and the
							// destination is less opaque then the shadow
							if (argbData[row * (width /* + size2 */) + col] >>> 24 == 0xFF
									&& argbData[(row + yOffset + iy) * (width /*
																			 * size2
																			 */) + col + xOffset + ix] >>> 24 < gColor >>> 24) {
								argbData[(row + yOffset + iy) * (width /*
																		 * +
																		 * size2
																		 */) + col + xOffset + ix] = gColor;
							}
						}
					}
				}
			}
		}
	}

	/**
	 * Get only the blue component of an image
	 * <p>
	 ** 
	 * <table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/before.png"></td>
	 * <td><img src="../../../resources/bluechannel.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @param image
	 * @return
	 */
	public static Image getBlueChannel(Image image) {
		int w = image.getWidth();
		int h = image.getHeight();
		int[] argb = ImageUtil.getImageRGB(image, w, h);
		getBlueChannel(argb);
		return Image.createRGBImage(argb, w, h, true);
	}

	public static void getBlueChannel(int[] argb) {
		doChannelMask(argb, 0x0000ff);
	}

	/**
	 *Get only the green component of an image
	 * <p>
	 *<table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/before.png"></td>
	 * <td><img src="../../../resources/greenchannel.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @param image
	 * @return
	 */
	public static Image getGreenChannel(Image image) {
		int w = image.getWidth();
		int h = image.getHeight();
		int[] argb = ImageUtil.getImageRGB(image, w, h);
		getGreenChannel(argb);
		return Image.createRGBImage(argb, w, h, true);
	}

	public static void getGreenChannel(int[] argb) {
		doChannelMask(argb, 0x00ff00);
	}

	/**
	 * Returns a mask of the image of the specified color with the same
	 * dimensions of the image.
	 * <p>
	 * <table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/before.png"></td>
	 * <td><img src="../../../resources/mask.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @param image
	 * @param maskColor
	 * @return
	 */
	public static Image getImageMask(Image image, int maskColor) {
		int[] argb = ImageUtil.getImageRGB(image, image.getWidth(), image.getHeight());
		getImageMask(argb, maskColor);
		return Image.createRGBImage(argb, image.getWidth(), image.getHeight(), true);
	}

	public static void getImageMask(int[] argb, int maskColor) {
		for (int i = 0; i < argb.length; i++) {
			int a = ((argb[i] & 0xff000000) >> 24);
			int r = ((argb[i] & 0x00ff0000) >> 16);
			int g = ((argb[i] & 0x0000ff00) >> 8);
			int b = ((argb[i] & 0x000000ff) >> 0);
			r = ColorUtil.getRed(maskColor);
			g = ColorUtil.getGreen(maskColor);
			b = ColorUtil.getBlue(maskColor);
			argb[i] = ColorUtil.toRGB(a, r, g, b);

		}
	}

	/**
	 * Get the red component of the image
	 * <p>
	 * </p>
	 * 
	 * @param image
	 * @return
	 */
	public static Image getRedChannel(Image image) {
		int w = image.getWidth();
		int h = image.getHeight();
		int[] argb = ImageUtil.getImageRGB(image, w, h);
		// doMask(argb, 0xff0000);
		getRedChannel(argb);
		return Image.createRGBImage(argb, w, h, true);
	}

	/**
	 * Creates a image with only the red channel of the the source
	 * <p>
	 * <table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/before.png"></td>
	 * <td><img src="../../../resources/redchannel.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @param image
	 *            source
	 * @return
	 */
	public static void getRedChannel(int[] argb) {
		doChannelMask(argb, 0xff0000);
	}

	/**
	 *Inverts the color of the image
	 * <p>
	 * <table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/gray.png"></td>
	 * <td><img src="../../../resources/inverted.png"></td>
	 * </tr>
	 * </table>
	 * 
	 * </p>
	 * 
	 *@see com.aphrodite.image.ColorUtil#invertColor(int)
	 * @param image
	 * @return
	 */
	public static Image invertImage(Image image) {
		int[] imageData = ImageUtil.getImageRGB(image, image.getWidth(), image.getHeight());
		invertImage(imageData);
		return Image.createRGBImage(imageData, image.getWidth(), image.getHeight(), false);
	}

	/**
	 * 
	 * @see com.aphrodite.image.ColorUtil#invertColor(int)
	 * @param argb
	 *            image data
	 */
	public static void invertImage(int[] argb) {
		for (int i = 0; i < argb.length; i++) {
			argb[i] = ColorUtil.invertColor(argb[i]);
		}
	}

	/**
	 * Set the luminosity of an image. (Transparency is maintained)
	 * <p>
	 * Luminosity is a measurement of brightness<br/> The following is with a
	 * luminosity value (<code>percent</code>) of 30
	 * <table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/gray.png"></td>
	 * <td><img src="../../../resources/luminosity.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @see com.aphrodite.image.ColorUtil#setLuminosity(int, int)
	 * @param image
	 * @param percent
	 *            <100
	 * @return
	 */
	public static Image setLuminosity(Image image, int percent) {
		int w = image.getWidth();
		int h = image.getHeight();
		int[] data = ImageUtil.getImageRGB(image, w, h);
		setLuminosity(data, percent);
		return Image.createRGBImage(data, w, h, true);
	}

	/**
	 * Increase the brightness of an image by specifying levels of each band
	 * <p>
	 * <b>note:</b>Image transparency is lost
	 * 
	 * </p>
	 * 
	 * @param image
	 * @param r
	 *            level to increase the red band, must be >256
	 * @param g
	 *            level to increase the green band, must be >256
	 * @param b
	 *            level to increase the blue band,must be >256
	 * @return
	 */
	public static final Image setLuminosity(Image image, int r, int g, int b) {
		// ############ tested by $t3p#3n on 30-july-08 #################//
		int[] argb = ImageUtil.getImageRGB(image, image.getWidth(), image.getHeight());
		setLuminosity(argb, r, g, b);
		return Image.createRGBImage(argb, image.getWidth(), image.getHeight(), true);
	}

	/**
	 * @see com.aphrodite.image.ColorUtil#setLuminosity(int, int)
	 * @param argb
	 *            Image data
	 * @param percent
	 *            <100
	 */
	public static void setLuminosity(int[] argb, int percent) {
		for (int i = 0; i < argb.length; i++) {
			argb[i] = ColorUtil.setLuminosity(argb[i], percent);
		}
	}

	/**
	 * Increase the brightness of an image by specifying levels of each band
	 * 
	 * @param argb
	 *            image data
	 * @param r
	 *            level to increase the red band, must be >256
	 * @param g
	 *            level to increase the green band, must be >256
	 * @param b
	 *            level to increase the blue band,must be >256
	 */
	public static final void setLuminosity(int[] argb, int r, int g, int b) {
		// ############ tested by $t3p#3n on 30-july-08 #################//
		int i = 0, nRed = 0, nGreen = 0, nBlue = 0;
		// int nAlpha = 0;
		for (i = 0; i < argb.length; i++) {
			// the scaling has to be done on unsigned values.
			// nAlpha = ((argb[i] & 0xff000000) >>
			// 24);//ImageUtil.getAlphaComponent
			// (argb[i]);// -
			// Byte.MIN_VALUE;
			nRed = ImageUtil.getRedComponent(argb[i]) - Byte.MIN_VALUE;
			nGreen = ImageUtil.getGreenComponent(argb[i]) - Byte.MIN_VALUE;
			nBlue = ImageUtil.getBlueComponent(argb[i]) - Byte.MIN_VALUE;
			// scale and convert back to signed byte values
			// nAlpha = Math.max(Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, nRed *
			// a / 256 + Byte.MIN_VALUE));
			nRed = Math.max(Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, nRed * r / 256 + Byte.MIN_VALUE));
			nGreen = Math.max(Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, nGreen * g / 256 + Byte.MIN_VALUE));
			nBlue = Math.max(Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, nBlue * b / 256 + Byte.MIN_VALUE));
			// argb[i] = (nAlpha) + (nRed << 16) + (nGreen << 8) + nBlue;
			argb[i] = ColorUtil.toRGB((byte) nRed, (byte) nGreen, (byte) nBlue);

		}
	}

	/**
	 * The saturation of a color is determined by a combination of light
	 * intensity and how much it is distributed across the spectrum of different
	 * wavelengths.
	 * <p>
	 * <table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/gray.png"></td>
	 * <td><img src="../../../resources/satur.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @see com.aphrodite.image.ColorUtil#setSaturation(int, int)
	 * @param image
	 * @param percent
	 * @return
	 */
	public static Image setSaturation(Image image, int percent) {
		int w = image.getWidth();
		int h = image.getHeight();
		int[] data = ImageUtil.getImageRGB(image, w, h);
		setSaturation(data, percent);
		return Image.createRGBImage(data, w, h, true);
	}

	/**
	 * @see com.aphrodite.image.ColorUtil#setSaturation(int, int)
	 * @param argb
	 *            Image data
	 * @param percent
	 *            <100
	 */
	public static void setSaturation(int[] argb, int percent) {
		for (int i = 0; i < argb.length; i++) {
			argb[i] = ColorUtil.setSaturation(argb[i], percent);
		}
	}

	/**
	 * Change the transparency of an image
	 * <p>
	 * Image with <code>level</code> set to 128
	 * <table border="0">
	 * <tr>
	 * <td><b>Before</b></td>
	 * <td><b>After</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/noalpha.png"></td>
	 * <td><img src="../../../resources/alpha.png"></td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @see com.aphrodite.image.ColorUtil#setTransparency(int, int)
	 * @param image
	 *            image to change the alpha level
	 * @param level
	 *            0 if fully opaque and 255 fully transparent
	 * @return
	 */
	public static final Image setTransparency(Image image, int level) {
		// ############ tested by $t3p#3n on 29-july-08 #################//
		int width = image.getWidth();
		int height = image.getHeight();
		int[] imageData = ImageUtil.getImageRGB(image, width, height);
		setTransparency(level, imageData);
		return Image.createRGBImage(imageData, width, height, true);
	}

	/**
	 * Sets the specified transparency to the RGB data.
	 * 
	 * @param transparency
	 *            the transparency between 0 (fully transparent) and 255 (fully
	 *            opaque)
	 * @param data
	 *            the RGB data
	 */
	private static void setTransparency(int transparency, int[] argb) {
		transparency = (transparency << 24); // is now 0xtt000000
		for (int i = 0; i < argb.length; i++) {
			argb[i] = (argb[i] & 0x00ffffff) | transparency;
		}
	}

	public static Image setTransparencyForOpaque(Image image, int level) {
		int w = image.getWidth();
		int h = image.getHeight();
		int[] argb = ImageUtil.getImageRGB(image, w, h);
		setTransparencyForOpaque(argb, level);
		return Image.createRGBImage(argb, w, h, true);
	}

	/**
	 * Sets the specified transparency to the RGB data, but only for pixels that
	 * are not full transparent already.
	 * 
	 * @param transparency
	 *            the transparency between 0 (fully transparent) and 255 (fully
	 *            opaque)
	 * @param data
	 *            the RGB data
	 */
	public static void setTransparencyForOpaque(int[] data, int transparency) {
		transparency = (transparency << 24); // is now 0xtt000000
		for (int i = 0; i < data.length; i++) {
			int pixel = data[i];
			if ((pixel & 0xff000000) != 0) {
				data[i] = (pixel & 0x00ffffff) | transparency;
			}
		}
	}

	/**
	 * Change the image color using the shiftType constants of this class
	 * <p>
	 * <img src="../../../resources/gray.png">
	 * <table border="0" pad="5">
	 * <tr>
	 * <td><b>SHIFT_BLUE_TO_GREEN</b></td>
	 * <td><b>SHIFT_BLUE_TO_RED</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/bluetogreen.png"></td>
	 * <td><img src="../../../resources/bluetored.png"></td>
	 * </tr>
	 * <tr>
	 * <td><b>SHIFT_GREEN_TO_BLUE</b></td>
	 * <td><b>SHIFT_GREEN_TO_RED</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/greentoblue.png"></td>
	 * <td><img src="../../../resources/greentored.png"></td>
	 * </tr>
	 * <tr>
	 * <td><b>SHIFT_RED_TO_BLUE</b></td>
	 * <td><b>SHIFT_RED_TO_GREEN</b></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/redtoblue.png"></td>
	 * <td><img src="../../../resources/redtogreen.png"></td>
	 * </tr>
	 * <tr>
	 * </table>
	 * </p>
	 * 
	 * @param source
	 * @param shiftType
	 * @return Image
	 */
	public static final Image shiftImageColor(Image source, int shiftType) {

		// we start by getting the image data into an int array - the number
		// of 32-bit ints is equal to the width multiplied by the height
		int[] rgbData = ImageUtil.getImageRGB(source, source.getWidth(), source.getHeight());
		// now go through every pixel and adjust its color
		for (int i = 0; i < rgbData.length; i++) {
			int p = rgbData[i];

			// split out the different byte components of the pixel by applying
			// a mask so we only get what we need, then shift it to make it
			// a normal number we can play around with
			int a = ((p & 0xff000000) >> 24);
			int r = ((p & 0x00ff0000) >> 16);
			int g = ((p & 0x0000ff00) >> 8);
			int b = ((p & 0x000000ff) >> 0);

			int ba = a, br = r, bb = b, bg = g; // backup copies

			// flip the colors around according to the operation required
			switch (shiftType) {
			case SHIFT_RED_TO_GREEN:
				g = r;
				r = bg;
				break;
			case SHIFT_RED_TO_BLUE:
				b = r;
				r = bb;
				break;
			case SHIFT_GREEN_TO_BLUE:
				g = b;
				b = bg;
				break;
			case SHIFT_GREEN_TO_RED:
				g = r;
				r = bg;
				break;
			case SHIFT_BLUE_TO_RED:
				b = r;
				r = bb;
				break;
			case SHIFT_BLUE_TO_GREEN:
				b = g;
				g = bb;
				break;
			}

			// shift all our values back in
			rgbData[i] = (a << 24) + (r << 16) + (g << 8) + b;
		}
		return Image.createRGBImage(rgbData, source.getWidth(), source.getHeight(), true);
		// return rgbData;
	}

	private ImageManipulation() {}

	// public static void drawText(Image image, String text,
	// com.aphrodite.util.BitmapFont font, int fontColor, int x,
	// int y) {
	// if (x > image.getWidth() || y > image.getHeight()) {
	// throw new
	// IllegalArgumentException("[ImageManipulation] Invalid x/y coordinates");
	// }
	// Graphics g = image.getGraphics();
	// g.setColor(fontColor);
	// // font.drawString(g, text, x, y, 0);
	// }
	//
	// public static void drawText(Image image, javax.microedition.lcdui.Font
	// font, int fontColor, int x, int y) {
	//
	// }

	// public static int[] setBorder(int[] argb, int width, int height, int
	// borderColor) {
	// for (int i = 0; i < height; i++) {
	//
	// for (int j = 0; j < width; j++) {
	// // argb[i] = borderColor;
	// argb[i * width + j] = borderColor;
	// }
	// }
	// return argb;
	// }
}
